﻿using MicroCom.Runtime;
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using P2P.Network.SocketServer;
using Newtonsoft.Json;
using System.Net.Sockets;
using P2P.Network;
using System.Net;
using P2P.Client.Enums;
using P2P.Client.Tasks;

namespace P2P.Client.Model
{
    public class SharedModel
    {
        public struct MainServer 
        {
            private readonly object _lock = new object();
            private ConnectStatus _connectStatus = ConnectStatus.Disconnected;
            private DateTime _LastActiveTime = DateTime.MinValue;

            public ConnectStatus ConnectStatus
            {
                get
                {
                    lock (_lock)
                    {
                        return _connectStatus;
                    }
                }
                set
                {
                    lock (_lock)
                    {
                        _connectStatus = value;
                    }
                }
            }

            public DateTime LastActiveTime
            {
                get
                {
                    lock (_lock)
                    {
                        return _LastActiveTime;
                    }
                }
                set
                {
                    lock (_lock)
                    {
                        _LastActiveTime = value;
                    }
                }
            }
            public MainServer()  { }

            public void Clear() 
            {
                ConnectStatus = ConnectStatus.Disconnected;
                LastActiveTime = DateTime.MinValue;
            }
        }

        public struct TurnServer 
        {
            private readonly object _lock = new object();
            private ConnectStatus _connectStatus = ConnectStatus.Disconnected;
            private DateTime _LastActiveTime = DateTime.MinValue;

            public ConnectStatus ConnectStatus
            {
                get
                {
                    lock (_lock)
                    {
                        return _connectStatus;
                    }
                }
                set
                {
                    lock (_lock)
                    {
                        _connectStatus = value;
                    }
                }
            }

            public DateTime LastActiveTime
            {
                get
                {
                    lock (_lock)
                    {
                        return _LastActiveTime;
                    }
                }
                set
                {
                    lock (_lock)
                    {
                        _LastActiveTime = value;
                    }
                }
            }
            public TurnServer() { }
        }

        public MainServer MainServerInfo = new ();

        public TurnServer TurnServerInfo = new ();

        public string AccountId { get; set; }

        public NatType NatType { get; set; }

        public Hosts Hosts { get; set; } = new();

        public UDPSocketListener UDPSocketListener { get; set; }

        private static readonly object _lock = new object();

        private static SharedModel? instance;

        private TimerTask _timerTask;

        private SharedModel()
        {
            _timerTask = new TimerTask(() => 
            {
                DateTime now = DateTime.Now;

                if (now.Ticks - MainServerInfo.LastActiveTime.Ticks > 30 * TimeSpan.TicksPerSecond)
                {
                    MainServerInfo.ConnectStatus = ConnectStatus.Disconnected;
                }

                if (now.Ticks - TurnServerInfo.LastActiveTime.Ticks > 30 * TimeSpan.TicksPerSecond) 
                {
                    TurnServerInfo.ConnectStatus = ConnectStatus.Disconnected;
                }
            },TimeSpan.FromSeconds(10));

            UDPSocketListener = new UDPSocketListener(IPAddress.Any, 0, ReceivedCallback);
            UDPSocketListener.Start();
        }

        private static void ReceivedCallback(Socket sender, DataEventArgs e)
        {
            byte[] buffer = e.Data;
            string bufStr = Encoding.UTF8.GetString(buffer);

            object result = ApiHandler.Process(sender, e.RemoteEndPoint, bufStr);

            if (result != null)
            {
                string msg = JsonConvert.SerializeObject(result, Formatting.None);

                sender.SendTo(Encoding.UTF8.GetBytes(msg), e.RemoteEndPoint);
            }
        }

        public static SharedModel Instance
        {
            get 
            {
                if (instance == null)
                {
                    lock (_lock)
                    {
                        if (instance == null)
                        {
                            instance = new SharedModel();
                        }
                    }
                }
                return instance;
            }
        }

        public void Configure(Hosts hosts) 
        {
            Hosts = hosts;
        }

        public void Configure(string accountId,Hosts hosts)
        {
            AccountId = accountId;
            Hosts = hosts;
        }
    }
}
